home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d12 / cgazv5n4.arc / HYPHEN.C < prev    next >
C/C++ Source or Header  |  1991-09-23  |  26KB  |  794 lines

  1. /*--- Listing 2 -------------------------- HYPHEN.C ------------
  2.  * An implementation of Knuth's  hyphenation algorithm
  3.  * Compiler:    Borland C/C++
  4.  * Author:      Allen I. Holub
  5.  * Compile-time switches:
  6.  *          DEBUG - if defined, a test driver is provided.
  7.  *                  When DEBUG is defined, the variables
  8.  *                  Debug and Verbose control the display
  9.  *                  of debugging information.
  10.  *
  11.  * (C) 1988, 1991, Allen I. Holub. All rights reserved.
  12.  *------------------------------------------------------------*/
  13.  
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <ctype.h>
  17. #include <string.h>
  18. #include "hyphen.h"
  19.  
  20. static void  consonants ( ctype *beg,   ctype *end      );
  21. static int   isweird    ( ctype x,      ctype y, ctype *p );
  22. static int   next       ( int cur_state,ctype cur_char  );
  23. static ctype nextch     ( ctype **pp,   ctype *endp     );
  24. static void  phyphen    ( ctype *start, ctype *end      );
  25. static ctype *prefix    ( ctype *beg,   ctype *end      );
  26. static ctype *suffix    ( ctype *beg,   ctype *end      );
  27.  
  28. #ifdef DEBUG          /* If > 1, then the hyphenated words */
  29.     int  Verbose = 2; /* are printed as they are parsed.   */
  30.  
  31.     static ctype *sgets ( uchar *prompt, ctype *buf, int maxch );
  32.     static void  bprint ( ctype *b , ctype *e );
  33. #else
  34.     int Verbose = 0;
  35. #endif
  36.  
  37. /*--------------------------------------------------------------
  38.  * if THING is #defined then words like "anything" will be split
  39.  * any-thing. On the other hand, if it is defined, then bathing
  40.  * is split ba-thing. You can't have every-thing. In the present
  41.  * version most of the -thing words are in the exception list, so
  42.  * THING can stay undefined.
  43.  *
  44.  * #define THING
  45.  */
  46. /*---------------------------------------------------------------
  47.  * Various pseudo-subroutines. HYPHEN defines a bit to set when a
  48.  * hyphen is inserted. HYPHENATE sets the bit, UNHYPHENATE clears
  49.  * it, HAS_HYPHEN tests for it. The ER macro checks for an "er"
  50.  * at the end of a word, it's used by the consonant-pair checking
  51.  * routine. Is consonant returns true if c is a consonant.
  52.  */
  53. #define ER(p,end) (CHAR(p[0])=='e' && \
  54.                    CHAR(p[1])=='r' && (p+1)==end)
  55.  
  56. /* The diphthongs ch, gh, ph, sh and th are treated as single
  57.  * consonants. The subroutine nextch() will map these two
  58.  * characters into a single character as follows:
  59.  */
  60.  
  61. #define CH ('z' + 1 )   /* {     0x7b  \173    */
  62. #define GH ('z' + 2 )   /* |     0x7c  \174    */
  63. #define PH ('z' + 3 )   /* }     0x7d  \175    */
  64. #define SH ('z' + 4 )   /* ~     0x7e  \176    */
  65. #define TH ('z' + 5 )   /* DEL   0x7f  \177    */
  66.  
  67. /*-------------------------------------------------------------
  68.  * State machine tables used by hyphen.c:
  69.  *
  70.  * There are two types of states. If the first entry is 0 then
  71.  * the state is an array holding the next state, indexed by the
  72.  * current input character. If the first entry is non-zero then
  73.  * the table is a list of char/next-state pairs. The first byte
  74.  * is a character and the second is the state to go to on that
  75.  * character. The leftmost byte in the list is the number of
  76.  * entries in the list. All unspecified transitions are to zero.
  77.  * -------------------------------------------------------------
  78.  * S(x) is used to initialize the state tables. It turned out to
  79.  * be easier to use a define than a typedef.
  80.  */
  81.  
  82.         /* Points to table for current state machine  */
  83. static uchar **States;
  84.  
  85. #define S(x)  static uchar (x)[]
  86.  
  87. S(s0 )={ 0, 84,84,84,1,1,84,84,84,84,84,84,84,84,
  88.          84,84,84,84,84,1,84, 84,84,84,84,84,84 };
  89. S(s1 )={ 0, 0,0,2,0,7,0,21,0,0,0,0,23,0,30,0,0,0,
  90.          33,38,45,0,0,0,0,49,0};
  91. S(s2 )={ 1, 'i', 3  };
  92. S(s3 )={ 1, 'p', 4  };
  93. S(s4 )={ 1, 'o', 5  };
  94. S(s5 )={ 1, 'c', 6  };
  95. S(s6 )={ 1, 's', 87 };
  96. S(s7 )={ 0, 0,0,0,0,0,0,0,0,0,0,0,8,0,0,0,4,0,10,
  97.          0,16,0,15,0,0,0,19 };
  98. S(s8 )={ 1, 'b', 9  };
  99. S(s9 )={ 1, 'a', 55 };
  100. S(s10)={ 2, 'e', 11, 'u', 14 };
  101. S(s11)={ 1, 'h', 12 };
  102. S(s12)={ 1, 'p', 6  };
  103. S(s13)={ 2, 'n', 81, 'r', 81 };
  104. S(s14)={ 1, 't', 87 };
  105. S(s15)={ 1, 'i', 14 };
  106. S(s16)={ 1, 'a', 17 };
  107. S(s17)={ 2, 'c', 18, 'l', 18 };
  108. S(s18)={ 0, 88,0,0,0,88,0,0,0,88,0,0,0,0,0,88,0,0,
  109.          0,0,0,88,0,0,0,88,0};
  110. S(s19)={ 1, 'i', 20 };
  111. S(s20)={ 1, 'l', 80 };
  112. S(s21)={ 1, 'n', 22 };
  113. S(s22)={ 1, 'i', 86 };
  114. S(s23)={ 2, 'a', 25, 'u', 24 };
  115. S(s24)={ 1, 'f', 83 };
  116. S(s25)={ 0, 0,0,87,0,0,0,0,0,26,0,0,0,0,27,0,0,0,0,
  117.          0,87,0,0,0,0,0,0 };
  118. S(s26)={ 2, 't', 87, 'c', 87 };
  119. S(s27)={ 1, 'o', 28 };
  120. S(s28)={ 1, 'i', 29 };
  121. S(s29)={ 1, 't', 89 };
  122. S(s30)={ 1, 'o', 31 };
  123. S(s31)={ 1, 'i', 32 };
  124. S(s32)={ 2, 't', 87, 'c', 6 };
  125. S(s33)={ 1, 'e', 34 };
  126. S(s34)={ 1, 'h', 35 };
  127. S(s35)={ 1, 'p', 36 };
  128. S(s36)={ 1, 'a', 37 };
  129. S(s37)={ 1, 'r', 87 };
  130. S(s38)={ 2, 's', 39, 'u', 41 };
  131. S(s39)={ 1, 'e', 40 };
  132. S(s40)={ 2, 'l', 83, 'n', 83 };
  133. S(s41)={ 1, 'o', 42 };
  134. S(s42)={ 1, 'i', 43 };
  135. S(s43)={ 1, 'c', 44 };
  136. S(s44)={ 0, 88,88,88,88,88,88,88,88,88,88,88,88,88,
  137.          88,88,88,88,88,87,88,88,88,88,88,88,88 };
  138. S(s45)={ 1, 'n', 46 };
  139. S(s46)={ 1, 'e', 47 };
  140. S(s47)={ 0, 0,0,0,87,0,0,0,0,48,0,0,0,83,0,0,0,0,0,
  141.          0,0,0,0,0,0,0,0 };
  142. S(s48)={ 1, 'c', 87 };
  143. S(s49)={ 0, 0,0,0,0,0,0,50,35,0,0,0,83,0,0,0,0,0,51,
  144.          0,0,0,0,0,0,0,0 };
  145. S(s50)={ 1, 'o', 87 };
  146. S(s51)={ 1, 'a', 52 };
  147. S(s52)={ 1, 'n', 53 };
  148. S(s53)={ 0, 88,88,88,88,81,88,88,88,88,88,88,88,88,
  149.          88,54,88,88,88,88,88,88,88,88,88,88,88 };
  150. S(s54)={ 1, 'i', 82 };
  151. S(s55)={ 0, 0,0,0,0,80,0,0,80,80,0,80,80,0,0,80,0,0,
  152.          0,0,13,80,80,80,80,80,0};
  153. static uchar *Suffixes[] =
  154. {
  155.         s0,  s1,  s2,  s3,  s4,  s5,  s6,  s7,  s8,  s9,
  156.         s10, s11, s12, s13, s14, s15, s16, s17, s18, s19,
  157.         s20, s21, s22, s23, s24, s25, s26, s27, s28, s29,
  158.         s30, s31, s32, s33, s34, s35, s36, s37, s38, s39,
  159.         s40, s41, s42, s43, s44, s45, s46, s47, s48, s49,
  160.         s50, s51, s52, s53, s54, s55
  161. };
  162. /*-------------------------------------------------------------*/
  163. S(p0 )= {1, 0, 0 };
  164. S(p1 )= {0, 0,2,4,6,9,0,0,13,22,0,0,26,29,39,41,45,
  165.          50,0,53,57,64,0,0,0,0,0 };
  166. S(p2 )= {1, 'e', 3 };
  167. S(p3 )= {0, 0,0,81,0,0,0,0,81,0,0,0,0,0,0,0,0,0,0,81,
  168.          0,0,0,81,0,0,0 };
  169. S(p4 )= {1, 'o', 5 };
  170. S(p5 )= {2, 'm', 87, 'n', 87 };
  171. S(p6 )= {1, 'i', 7 };
  172. S(p7 )= {1, 's', 8 };
  173. S(p8 )= {0, 85,85,85,85,85,85,85, 0,85,85,85,85,85,
  174.          85,85,85,85,85,85,85,85,85,85,85, 0,85 };
  175. S(p9 )= {2, 'q', 10, 'x', 87 };
  176. S(p10)= {1, 'u', 11 };
  177. S(p11)= {1, 'i', 12 };
  178. S(p12)= {0, 81,81,81,81,81,81,81,81,81,81,81,81,81,
  179.          81,81,81,81,81,81,81,81, 0,81,81,81,81 };
  180. S(p13)= {0, 14,0,0,0,0,0,0,0,0,0,0,0,0,0,16,0,0,0,
  181.          0,0,0,0,0,0,19,0 };
  182. S(p14)= {1, 'n', 15 };
  183. S(p15)= {1, 'd', 87 };
  184. S(p16)= {1, 'r', 17 };
  185. S(p17)= {1, 's', 18 };
  186. S(p18)= {1, 'e', 87 };
  187. S(p19)= {1, 'p', 20 };
  188. S(p20)= {1, 'e', 21 };
  189. S(p21)= {1, 'r', 84 };
  190. S(p22)= {2, 'n', 69, 'm', 86 };
  191. S(p23)= {0, 81,0,0,0,0,81,81,0,0,0,0,81,81,0,0,0,0,
  192.          0,0,0,0,0,0,0,0,0 };
  193. S(p24)= {2, 'e', 21, 'r', 25 };
  194. S(p25)= {1, 'o', 87 };
  195. S(p26)= {1, 'e', 27 };
  196. S(p27)= {1, 'x', 35 };
  197. S(p28)= {1, 'i', 87 };
  198. S(p29)= {0, 30,0,0,0,0,0,0,0,36,0,0,0,0,0,0,0,0,0,0,
  199.          0,37,0,0,0,0,0 };
  200. S(p30)= {0, 0,0,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  201.          33,0,0,0,35,0,0 };
  202. S(p31)= {1, 'r', 32 };
  203. S(p32)= {1, 'o', 83 };
  204. S(p33)= {1, 'h', 34 };
  205. S(p34)= {1, 'e', 82 };
  206. S(p35)= {1, 'i', 82 };
  207. S(p36)= {1, 'n', 35 };
  208. S(p37)= {1, 'l', 38 };
  209. S(p38)= {1, 't', 65 };
  210. S(p39)= {1, 'o', 40 };
  211. S(p40)= {1, 'n', 86 };
  212. S(p41)= {2, 'u', 42, 'v', 43 };
  213. S(p42)= {1, 't', 87 };
  214. S(p43)= {1, 'e', 44 };
  215. S(p44)= {1, 'r', 86 };
  216. S(p45)= {1, 's', 46 };
  217. S(p46)= {1, 'e', 47 };
  218. S(p47)= {1, 'u', 48 };
  219. S(p48)= {1, 'd', 49 };
  220. S(p49)= {1, 'o', 83 };
  221. S(p50)= {1, 'u', 51 };
  222. S(p51)= {1, 'a', 52 };
  223. S(p52)= {1, 'd', 87 };
  224. S(p53)= {0, 0,0,0,0,54,0,0,0,0,0,0,0,0,0,55,0,0,0,
  225.          0,0,56,0,0,0,0,0 };
  226. S(p54)= {1, 'm', 28 };
  227. S(p55)= {1, 'm', 18 };
  228. S(p56)= {2, 'b', 87, 'p', 20 };
  229. S(p57)= {2, 'h', 58, 'r', 61 };
  230. S(p58)= {1, 'e', 59 };
  231. S(p59)= {1, 'r', 60 };
  232. S(p60)= {1, 'e', 87 };
  233. S(p61)= {2, 'a', 62, 'i', 68 };
  234. S(p62)= {1, 'n', 63 };
  235. S(p63)= {1, 's', 23 };
  236. S(p64)= {1, 'n', 66 };
  237. S(p65)= {1, 'i', 83 };
  238. S(p66)= {0, 85,85,85,70,85,85,85,85, 0,85,85,85,85,
  239.          85,85,85,85,85,85,85,85,85,85,85,85,85 };
  240. S(p67)= {1, 'r', 87 };
  241. S(p68)= {0, 81,0,0,0,0,81,0,0,0,0,0,0,0,0,0,0,0,0,
  242.          0,0,81,0,0,0,0,0 };
  243. S(p69)= {0, 85,85,85,85,85,85,85,85,85,85,85,85,85,
  244.          85,85,85,85,85,85,24,85,85,85,85,85,85 };
  245. S(p70)= {1, 'e', 67 };
  246.  
  247. static uchar *Prefixes[] =
  248. {
  249.         p0,  p1,  p2,  p3,  p4,  p5,  p6,  p7,  p8,  p9,
  250.         p10, p11, p12, p13, p14, p15, p16, p17, p18, p19,
  251.         p20, p21, p22, p23, p24, p25, p26, p27, p28, p29,
  252.         p30, p31, p32, p33, p34, p35, p36, p37, p38, p39,
  253.         p40, p41, p42, p43, p44, p45, p46, p47, p48, p49,
  254.         p50, p51, p52, p53, p54, p55, p56, p57, p58, p59,
  255.         p60, p61, p62, p63, p64, p65, p66, p67, p68, p69,
  256.         p70
  257. };
  258.  
  259. /*-------------------------------------------------------------*/
  260.  
  261. #ifdef DEBUG
  262. static int  Debug = 0;  /* True if debug diagnostics
  263.                            are to be printed  */
  264. #endif
  265. /*-------------------------------------------------------------*/
  266. static uchar Vt[] =
  267.    /* a b c d e f g h i j k l m n o p q r s t u v w x y z */
  268.     { 1,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0 };
  269.  
  270. #define ISVOWEL(c)      ( islower(CHAR(c)) && Vt[CHAR(c) - 'a'] )
  271. #define ISCONSONANT(c)  ((c) && !ISVOWEL(c))
  272. /*-------------------------------------------------------------*/
  273. static  ctype   *suffix( ctype *beg, ctype *end )
  274. {
  275.   /*  Split off any suffixes, using the "Suffixes" state table
  276.    *  (defined below) to recognize them.
  277.    */
  278.  
  279.     ctype    *p, c, c2    ;
  280.     unsigned state, times ;
  281.  
  282.     state  = 1;
  283.     times  = 0;
  284.     States = Suffixes;
  285.  
  286.     for( p = end; p >= beg ;)
  287.     {
  288.         state = next( state, c = CHAR(*p--) );
  289.  
  290.         if( !('a' <= c && c <= 'z') ) /* Words containing non- */
  291.                 return end;           /* lower-case characters */
  292.                                       /* won't be hyphenated.  */
  293.  
  294.      /* Test for state 0 with an if instead of in the switch.
  295.       * Lattice C will make the switch very inefficient if the
  296.       * range of case values is too high. State 0 is weird;
  297.       * we use it to test for trailing e or ed if we've failed.
  298.       * The first time through we set p to end so we can check
  299.       * the end of the word. The third time through we abort.
  300.       * This means that we'll retry if the word ends -s -e -d
  301.       * -ed -de -dd -ee. The extra endings shouldn't matter.
  302.       */
  303.  
  304.         if( state == 0 )
  305.         {
  306.             if( times == 0 )
  307.                 p = end ;     /* Strip trailing e or d */
  308.  
  309.             else if( times == 1 ) /* Strip trailing ed, de */
  310.                 p = end-1 ;   /* ee, or dd             */
  311.             else
  312.                 return end;
  313.  
  314.             times++;
  315.         }
  316.  
  317.         switch( state )
  318.         {
  319.         case 86:
  320.              /* Process -ing. This could have been done with
  321.               * more states but it seemed easier to do it here.
  322.               * I've modified the Knuth algorithm to not
  323.               * hyphenate in front of a th that precedes
  324.               * an -ing to make any-thing split right. Note
  325.               * that bathing will split incorrectly though.
  326.               * #define THING to enable this function.
  327.               * On entry p points at the character preceding
  328.               * the 'i'.
  329.               */
  330.  
  331.                 c =  CHAR(  p[0]  );
  332.                 c2 = CHAR(  p[-1] );
  333.  
  334.                 if( p - beg < 3  )
  335.                     return end; /* FAIL */
  336. #ifdef THING
  337.                 else if( c == 'h'  &&  c2 == 't' )
  338.                     p -= 2;
  339. #endif
  340.                 else if(     ( c == c2 ) && ( !ISVOWEL(c) )
  341.                           && ( c != 'f') && ( c != 's'    )
  342.                           && ( c != 'l') && ( c != 'z'    ) )
  343.                 {
  344.                     --p;
  345.                 }
  346.                 else if ( c == 'l'  &&  strchr("bcdfghkptz", c2))
  347.                     p -= (c2 == 'k' && CHAR(p[-2]) == 'c') ?
  348.                                     1 : 2;
  349.  
  350.                 /* fall through to state 87 */
  351.  
  352.         case 87:
  353.                 HYPHENATE( *(p+1) );
  354.                 return p;
  355.  
  356.         case 88:
  357.                 HYPHENATE( *(p+2) );
  358.                 return( p + 1 );
  359.  
  360.         case 89:
  361.                 HYPHENATE( *(p+5) );
  362.                 HYPHENATE( *(p+1) );
  363.                 return p;
  364.  
  365.         case 82: p++;   /* p += 3 */
  366.         case 81: p++;   /* p += 2 */
  367.         case 80: p++;   /* P += 1 */
  368.         case 83:
  369.                  end = p ;
  370.                  HYPHENATE( *(p + 1) );
  371.                  state = 1;
  372.                  break;
  373.         case 84:                        /* FAILURE STATE */
  374.                 return end;
  375.         }
  376.     }
  377.  
  378.     return end;
  379. }
  380. /*-------------------------------------------------------------*/
  381. static ctype *prefix( ctype *beg, ctype *end )
  382. {
  383.   /* Split off any prefixes, using the "Prefixes" state table */
  384.  
  385.     ctype *p;
  386.     int   state = 1;
  387.  
  388.     States = Prefixes;
  389.     for( p = beg ; p < end ;)
  390.     {
  391.         switch( state = next(state, CHAR(*p++)) )
  392.         {
  393.         case 82: HYPHENATE( *p ); HYPHENATE( *(p-1) ) ; return p;
  394.         case 83: HYPHENATE( *p ); HYPHENATE( *(p-2) ) ; return p;
  395.         case 84: HYPHENATE( *p ); HYPHENATE( *(p-3) ) ; return p;
  396.  
  397.         case 81: --p;
  398.         case 87: HYPHENATE( *p );
  399.                  return p;
  400.  
  401.         case 85: --p;
  402.         case 86: beg   = p;
  403.                  state = 1;
  404.         case 69: HYPHENATE( *p );
  405.                  break;
  406.  
  407.         case 70: HYPHENATE( *(p-1) );
  408.                  break;
  409.  
  410.         /*      The following states don't actually exist.
  411.          *      Putting them in the table causes the compiler
  412.          *      to compile the switch more efficiently.
  413.          */
  414.  
  415.         case 71: case 72: case 73: case 74: case 75:
  416.         case 76: case 77: case 78: case 79:
  417.                 break;
  418.         }
  419.     }
  420.     return beg; /* Failure */
  421. }
  422.  
  423. /*--------------------------------------------------------------
  424.  * This table is used to find exceptions to the VCCV rule. It is
  425.  * indexed by the first of the two consonants & contains pointers
  426.  * to strings, any character of which will form an exception if
  427.  * it's the second consonant.
  428.  */
  429.  
  430. static uchar    *vccv_except[] =
  431. {
  432.     /* a  */    ""      ,
  433.     /* b  */    "lr"    ,
  434.     /* c  */    "lr"    ,
  435.     /* d  */    "gr"    ,
  436.     /* e  */    ""      ,
  437.     /* f  */    "lr"    ,
  438.     /* g  */    "lr"    ,
  439.     /* h  */    ""      ,
  440.     /* i  */    ""      ,
  441.     /* j  */    ""      ,
  442.     /* k  */    "n"     ,
  443.     /* l  */    "kq"    ,
  444.     /* m  */    ""      ,
  445.     /* n  */    "{kx"   , /* CH, k, x }*/
  446.     /* o  */    ""      ,
  447.     /* p  */    "lr"    ,
  448.     /* q  */    ""      ,
  449.     /* r  */    "k"     ,
  450.     /* s  */    "pq"    ,
  451.     /* t  */    "{r"    , /* CH, r    }*/
  452.     /* u  */    ""      ,
  453.     /* v  */    ""      ,
  454.     /* w  */    "hlnr"  ,
  455.     /* x  */    ""      ,
  456.     /* y  */    ""      ,
  457.     /* z  */    ""      ,
  458.     /* CH */    "lr"    ,
  459.     /* GH */    "t"     ,
  460.     /* PH */    "r"     ,
  461.     /* SH */    ""      ,
  462.     /* TH */    "r"
  463. };
  464. /*-------------------------------------------------------------*/
  465. static ctype nextch( ctype **pp, ctype *endp )
  466. {
  467.     /*  Advance *pp (one for a vowel or consonant, 2 for a
  468.      *  diphthong) and return the character we've just skipped.
  469.      *  Diphthongs are mapped to the single characters: TH, SH,
  470.      *  PH, CH or GH. Returns 0 once *pp reaches or passes endp.
  471.      */
  472.  
  473.     ctype *p   = *pp;
  474.     ctype rval = CHAR(*p++);
  475.  
  476.     if( *pp > endp )
  477.         return (ctype)0;
  478.  
  479.     if( CHAR(*p)  ==  'h' )
  480.     {
  481.         switch( (int) rval )
  482.         {
  483.         case 't':       rval = TH;      p++;    break;
  484.         case 's':       rval = SH;      p++;    break;
  485.         case 'p':       rval = PH;      p++;    break;
  486.         case 'c':       rval = CH;      p++;    break;
  487.         case 'g':       rval = GH;      p++;    break;
  488.         }
  489.     }
  490.     *pp = p;
  491.     return rval;
  492. }
  493. /*-------------------------------------------------------------*/
  494. static int isweird( ctype x, ctype y, ctype *p )
  495. {
  496.     /* Return true if the string pointed to by p ends in:
  497.      *  XYer XYers XYage XYages XYest
  498.      *
  499.      * where XY is one of the pairs:
  500.      *  ft ld mp nd ng ns nt rg rm rn rt st
  501.      */
  502.  
  503.     unsigned c1, c2, c3;
  504.  
  505.     c1 =  CHAR(*p++);
  506.     c2 =  CHAR(*p++);
  507.     c3 =  CHAR(*p  );
  508.     x  =  CHAR(x);
  509.     y  =  CHAR(y);
  510.  
  511.     return( (
  512.                     ( c1 == 'e' && c2 == 'r'           )
  513.                 ||  ( c1 == 'a' && c2 == 'g' &&  c3 == 'e' )
  514.                 ||  ( c1 == 'e' && c2 == 's' &&  c3 == 't' )
  515.             )
  516.               &&
  517.             (       ( x == 'f'  && y == 't' )
  518.                 ||  ( x == 'l'  && y == 'd' )
  519.                 ||  ( x == 'm'  && y == 'p' )
  520.                 ||  ( x == 's'  && y == 't' )
  521.                 ||  ( x == 'n'  && strchr( "dgst", CHAR(y)) )
  522.                 ||  ( x == 'r'  && strchr( "gmnt", CHAR(y)) )
  523.             )
  524.     );
  525. }
  526. /*-------------------------------------------------------------*/
  527. static  void    consonants( ctype *beg, ctype *end )
  528. {
  529.     /*  Hyphenate consonant pairs.
  530.      *  Look for a VCC pattern: use the state machine:
  531.      *
  532.      *           V          C        C
  533.      * Start ──┬─> 1 ─────> 2 ───┬─> 3 ───┬─> 4
  534.      *     │   │C    │ V    │    │ V      │
  535.      *     │   │     │      │    │        │  (no fetch )
  536.      *     │   v     │      v    │        v
  537.      *     ├─<─┘     └──────<────┘        │
  538.      *     │                              │
  539.      *     └────────────────<─────────────┘
  540.      *
  541.      *  The machine is implemented explicitly (with whiles, ifs,
  542.      *  and such) rather than with a table-driven engine.
  543.      *  A new character is fetched in states 1, 2, and 3 (but
  544.      *  not 4). When you exit (reach state 4) c1 and c2 will hold
  545.      *  the two consonants, cp will point to the beginning of the
  546.      *  second consonant, beg will point just past the second
  547.      *  consonant.
  548.      */
  549.  
  550.     ctype  c1, c2, *cp;
  551.     uchar *p;
  552.  
  553.     while( 1 )
  554.     {
  555.     state1:
  556.  
  557.         do {
  558.                 c2 = nextch( &beg, end );
  559.         } while( ISCONSONANT(c2) );
  560.  
  561.         do {
  562.             for( c1 = c2; ISVOWEL(c1) ; )
  563.             {
  564.                 cp = beg;
  565.                 c1 = nextch( &beg, end );
  566.             }
  567.             if(c1 == 'q' && *beg == 'u')        /*  Vqu */
  568.             {
  569.                 HYPHENATE( *cp );       /*         V-qu */
  570.                 nextch( &beg, end );    /*   skip the u */
  571.                 goto state1;
  572.             }
  573.             cp = beg ;
  574.             c2 = nextch(&beg, end) ;
  575.  
  576.         } while( ISVOWEL(c2) ) ;
  577.  
  578.         if( !c1 || !c2 )
  579.                 break;
  580.  
  581.         /* At this point we have found a Vowel-Cons-Cons
  582.          * sequence. C1 and c2 hold the left and right
  583.          * consonant respectively. Beg points at the
  584.          * character following the second consonant.
  585.          * Cp points at the second consonant.
  586.          */
  587.  
  588.         if( c1 == 'c' &&  c2 == 'k' )           /*        Vck  */
  589.         {
  590.             if( *beg )
  591.                 HYPHENATE( *beg );              /*        Vck- */
  592.         }
  593.         else if( c1 == c2 )                     /*     ller(s) */
  594.         {
  595.             if((c1!='l' && c1!='s') || (ISVOWEL(*beg) &&
  596.                !ER(beg,end)))
  597.                  HYPHENATE( *cp );
  598.         }
  599.         else if( ISVOWEL( *beg ) )              /*        VCCV */
  600.         {
  601.             if( ! isweird(c1, c2, beg) )
  602.             {
  603.                 if( !((p = vccv_except[ (int)c1 - 'a' ])
  604.                                         && strchr(p, (int)c2) ))
  605.                 HYPHENATE( *cp );
  606.             }
  607.         }
  608.     }
  609.     UNHYPHENATE( *end );
  610. }
  611. /*-------------------------------------------------------------*/
  612. static  void    phyphen( ctype *start, ctype *end )
  613. {
  614.     /*  Print a word with all the hyphens visible */
  615.  
  616.     for(; *start && start <= end  ; putchar( CHAR(*start++) ))
  617.         if( HAS_HYPHEN(*start) )
  618.             putchar('-');
  619.  
  620.     putchar('\n');
  621. }
  622. /*-------------------------------------------------------------*/
  623. int hyphen( ctype *beg, ctype *end )
  624. {
  625.     /*  Hyphenate the word delineated by beg and end: First
  626.      *  strip suffixes, then strip a trailing s e or ed,
  627.      *  then strip prefixes. Only words with more than four
  628.      *  letters and which consist of lower case letters only
  629.      *  (no NULLs) will be hyphenated. 0 is returned if no
  630.      *  attempt was made to hyphenate the word, 1 otherwise.
  631.      *  If the word has already been hyphenated, 1 is returned
  632.      *  but the word is not modified.
  633.      */
  634.  
  635.     ctype *prefixp, *suffixp;
  636.     int   c;
  637.  
  638.     if( end-beg <= 4 )
  639.         return 0;
  640.  
  641.     for( prefixp = beg; prefixp <= end ; prefixp++ )
  642.     {
  643.         if( HAS_HYPHEN(*prefixp) )
  644.             return 1;
  645.         if( !islower( CHAR(*prefixp) ) || !ISCHAR(*prefixp) )
  646.             return 0;
  647.     }
  648.     if( !exception( beg, end ) )
  649.     {
  650.         suffixp = suffix(beg,end);   /* Hyphenate and remove */
  651.                                      /* any suffixes.        */
  652.         if( suffixp == end )
  653.         {
  654.             /*  If root has a trailing s e or ed remove it
  655.              *  before applying any other rules
  656.              */
  657.             c = CHAR(*end);
  658.  
  659.             if( c == 's'  ||  c  == 'e' )
  660.                 suffixp = end - 1 ;
  661.             else if( CHAR(end[-1]) == 'e'  &&  c == 'd' )
  662.                 suffixp = end - 2 ;
  663.         }
  664.         prefixp = prefix( beg, suffixp); /* Hyphenate and */
  665.                                      /* remove any prefixes  */
  666. #ifdef DEBUG
  667.       /*------------------------------------------------------*/
  668.       /*  Print the word in the form:  prefixes/root/suffixes */
  669.       /*                                                      */
  670.       bprint( beg, prefixp-1  );      /* Print prefixes       */
  671.       putchar('/');
  672.       bprint( prefixp, suffixp );     /* Print middle         */
  673.       putchar('/');
  674.       bprint( suffixp+1, end   );     /* Print suffixes       */
  675.       printf("=\t");
  676.       /*------------------------------------------------------*/
  677. #endif
  678.         if( (suffixp - prefixp)  >=  3 )
  679.         {
  680.             /* Apply the consonant pairs rules only if the word
  681.              * has at least 4 letters in it (after prefixes
  682.              * and suffixes have been removed
  683.              */
  684.             consonants( prefixp, suffixp );
  685.         }
  686.     }
  687.     if( Verbose > 1 )
  688.             phyphen( beg, end );
  689.     return 1;
  690. }
  691. /*-------------------------------------------------------------*/
  692. static  int next( int cur_state, ctype cur_char )
  693. {
  694.     /*  Given the current state and the current input character
  695.      *  return the next state. The global variable States must
  696.      *  point at the correct state table (Suffixes or Prefixes).
  697.      */
  698.  
  699.     uchar *p = States[ cur_state ] ;
  700.     int rval, i, c ;
  701.  
  702.     c = CHAR(cur_char);
  703.  
  704. #ifdef DEBUG
  705.     if( Debug )
  706.         printf("%s: Current state = %d, Input char = <%c>, ",
  707.                       States == Prefixes ? "PREFIX" : "SUFFIX",
  708.                       cur_state, c );
  709. #endif
  710.  
  711.     if( !*p )
  712.         rval = (int)( p[ (c - 'a') + 1 ] );
  713.     else
  714.     {
  715.         for( rval = 0, i = *p++; --i >= 0 ; p += 2 )
  716.             if( c == p[0] )
  717.             {
  718.                 rval = p[1];
  719.                 break;
  720.             }
  721.     }
  722.  
  723. #ifdef DEBUG
  724.     if( Debug )
  725.         printf("Next state = %d\n", rval );
  726. #endif
  727.     return( rval );
  728. }
  729. /*===========================================================*/
  730. /*   MISC. DEBUGGING ROUTINES (INCLUDING A MAIN MODULE):     */
  731.  
  732. #ifdef DEBUG
  733.  
  734. static void bprint( ctype *b , ctype *e )
  735. {
  736.     /* Print all characters between b and e, inclusive.
  737.      * Can't use printf because it expects char* for %s, not
  738.      * CYPTE*.
  739.      */
  740.  
  741.     while( b <= e && *b )
  742.         putchar( CHAR(*b++) );
  743. }
  744. /*-----------------------------------------------------------*/
  745. static ctype *sgets( uchar *prompt, ctype *buf, int maxch )
  746. {
  747.     /* Print prompt to stdout, then fill buf fm stdin. Get at
  748.      * most maxch characters. Return pointer to string terminator
  749.      * (i.e., the null at the end of the string) or 0 on end of
  750.      * file. We set a high bit to make sure that only the bottom
  751.      * seven bits of the character are used in the algorithm.
  752.      */
  753.  
  754.     int c;
  755.     ctype *start;
  756.  
  757.     start = buf;
  758.     printf( prompt );
  759.  
  760.     while( (c = getchar()) != EOF && !isalpha(c) )
  761.         ;
  762.  
  763.     if( c == EOF )
  764.         return NULL;
  765.     else
  766.         *buf++ = c;
  767.  
  768.     while( (c = getchar()) != EOF  &&
  769.             !isspace(c)  && --maxch > 0 )
  770.         *buf++ = c;
  771.     *buf = 0;
  772.     return (c == EOF && start == buf) ? NULL : buf ;
  773. }
  774. /*------------------------------------------------------------*/
  775. int main(int argc)
  776. {
  777.     ctype buf[132], *p;
  778.  
  779.     /* Note that ctype should be char for this to work */
  780.  
  781.     if( argc > 1 )
  782.         Debug = 1;
  783.  
  784.     while( p = sgets( "word: ", buf, 132 ) )
  785.     {
  786.         if( !*buf )
  787.             printf("\n");
  788.         else if( p-- > buf )
  789.             hyphen( buf, p );
  790.     }
  791.     return 0;
  792. }
  793.  
  794. #endif /* DEBUG */